package com.babel.util.lide;

import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import sun.misc.BASE64Decoder;

/**
 * RSA加密工具类
 * @author lyj
 * @date 2017年5月10日---下午5:46:54
 */
public class RSAEncryptUtil {

    /** 算法名称 */
    private static final String ALGORITHOM = "RSA";

    /** 默认的安全服务提供者 */
    private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider();

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /** 
     * 得到公钥 
     *  
     * @param key 
     *            密钥字符串（经过base64编码） 
     * @throws Exception 
     */
    public static PublicKey getPublicKey(String key) throws Exception {
	byte[] keyBytes;
	keyBytes = (new BASE64Decoder()).decodeBuffer(key);
	X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
	KeyFactory keyFactory = KeyFactory.getInstance("RSA");
	PublicKey publicKey = keyFactory.generatePublic(keySpec);
	return publicKey;
    }

    /** 
     * 得到私钥 
     *  
     * @param key 
     *            密钥字符串（经过base64编码） 
     * @throws Exception 
     */
    public static PrivateKey getPrivateKey(String key) throws Exception {
	byte[] keyBytes;
	keyBytes = (new BASE64Decoder()).decodeBuffer(key);
	PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
	KeyFactory keyFactory = KeyFactory.getInstance("RSA");
	PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
	return privateKey;
    }

    /**
     * 使用给定的公钥加密给定的字符串。
     * <p />
     * 若 {@code publicKey} 为 {@code null}，或者 {@code plaintext} 为 {@code null} 则返回 {@code
     * null}。
     * 
     * @param publicKey 给定的公钥。
     * @param plaintext 字符串。
     * @return 给定字符串的密文。
     */
    public static String encryptString(PublicKey publicKey, String plaintext) {
	if (publicKey == null || plaintext == null) {
	    return null;
	}
	byte[] data = plaintext.getBytes();
	try {
	    byte[] en_data = encryptByPublicKey(publicKey, data);
	    return new String(Hex.encodeHex(en_data));
	} catch (Exception ex) {
	}
	return null;
    }

    /**
     * 使用给定的私钥解密给定的字符串。
     * <p />
     * 若私钥为 {@code null}，或者 {@code encrypttext} 为 {@code null}或空字符串则返回 {@code null}。
     * 私钥不匹配时，返回 {@code null}。
     * 
     * @param privateKey 给定的私钥。
     * @param encrypttext 密文。
     * @return 原文字符串。
     */
    public static String decryptString(PrivateKey privateKey, String encrypttext) {
	if (privateKey == null) {
	    return null;
	}
	try {
	    byte[] en_data = Hex.decodeHex(encrypttext.toCharArray());
	    byte[] data = decryptByPrivateKey(privateKey, en_data);
	    return new String(data);
	} catch (Exception ex) {
	}
	return null;
    }

    /**
     * <p>
     * 公钥加密
     * </p>
     * 
     * @param data 源数据
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(Key publicK, byte[] data) throws Exception {

	Cipher cipher = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
	cipher.init(Cipher.ENCRYPT_MODE, publicK);
	int inputLen = data.length;
	ByteArrayOutputStream out = new ByteArrayOutputStream();
	int offSet = 0;
	byte[] cache;
	int i = 0;
	// 对数据分段加密
	while (inputLen - offSet > 0) {
	    if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
		cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
	    } else {
		cache = cipher.doFinal(data, offSet, inputLen - offSet);
	    }
	    out.write(cache, 0, cache.length);
	    i++;
	    offSet = i * MAX_ENCRYPT_BLOCK;
	}
	byte[] encryptedData = out.toByteArray();
	out.close();
	return encryptedData;
    }

    /**
     * <P>
     * 私钥解密
     * </p>
     * 
     * @param encryptedData 已加密数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(Key privateK, byte[] encryptedData) throws Exception {

	Cipher cipher = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
	cipher.init(Cipher.DECRYPT_MODE, privateK);
	int inputLen = encryptedData.length;
	ByteArrayOutputStream out = new ByteArrayOutputStream();
	int offSet = 0;
	byte[] cache;
	int i = 0;
	// 对数据分段解密
	while (inputLen - offSet > 0) {
	    if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
		cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
	    } else {
		cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
	    }
	    out.write(cache, 0, cache.length);
	    i++;
	    offSet = i * MAX_DECRYPT_BLOCK;
	}
	byte[] decryptedData = out.toByteArray();
	out.close();
	return decryptedData;
    }

}
